package main

import (
	"fmt"
	"math/rand"
	"time"
)

func genMsg(name string) chan string {
	msg := make(chan string)
	go func() {
		i := 97
		for {
			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
			msg <- fmt.Sprintf("%s gen char %d", name, i)
			i++
		}
	}()
	return msg
}

func msgGen(name string, done chan struct{}) chan string {
	msg := make(chan string)
	go func() {
		i := 97
		for {
			select {
			case <-time.After(time.Duration(rand.Intn(2000)) * time.Millisecond):
				msg <- fmt.Sprintf("%s gen char %d", name, i)
			case <-done:
				fmt.Println("cleaning up")
				time.Sleep(2 * time.Second)
				fmt.Println("cleanup done")
				//双向操作
				done <- struct{}{}
				return
			}
			i++
		}
	}()
	return msg
}

// 合二为一的功能
func fanIn(chs ...chan string) chan string {
	c := make(chan string)
	for _, ch := range chs {
		//通过函数传参的方式规避了使用同一个迭代变量ch
		go func(in chan string) {
			for {
				c <- <-in
			}
		}(ch)
	}

	return c
}

func fanInBySelect(c1, c2 chan string) chan string {
	c := make(chan string)
	go func() {
		for {
			select {
			case temp := <-c1:
				c <- temp
			case temp := <-c2:
				c <- temp
			}

		}
	}()
	return c
}

func nonBlockingWait(c chan string) (string, bool) {
	//select 加了 default 就会变成非阻塞等待
	select {
	case m := <-c:
		return m, true
	default:
		return "", false
	}
}

func timeoutWait(c chan string, timeout time.Duration) (string, bool) {
	select {
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
	//m1 := genMsg("server a")
	//m2 := genMsg("server b")
	//m := fanIn(m1, m2)
	//m := fanInBySelect(m1, m2)

	done := make(chan struct{})
	m1 := msgGen("service c", done)
	//for {
	//fmt.Println(<-m)

	//====== 非阻塞等待示例 ========
	//fmt.Println(<-m1)
	//if m, ok := nonBlockingWait(m2); ok {
	//	fmt.Println(m)
	//} else {
	//	fmt.Println("no message from service b")
	//}
	//}
	//====== 退出示例 ========
	for i := 0; i < 5; i++ {
		if m, ok := timeoutWait(m1, time.Second); ok {
			fmt.Println(m)
		} else {
			fmt.Println("timeout")
		}
	}
	done <- struct{}{}
	<-done
}
